package cinvestav.adviseme.webservice;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;

import cinvestav.adviseme.controller.LugarController;
import cinvestav.adviseme.datamodel.Comentario;
import cinvestav.adviseme.datamodel.Lugar;
import cinvestav.adviseme.datamodel.LugarAgregado;
import cinvestav.adviseme.datamodel.Persona;
import cinvestav.adviseme.datamodel.Solicitud;
import cinvestav.adviseme.datamodel.TipoLugar;
import cinvestav.adviseme.util.DBException;
import cinvestav.adviseme.util.DataBaseDictionary;
import cinvestav.adviseme.util.QueryParamDictionary;

@Path("/lugar")
public class LugarWebService {
	protected LugarController lugarController;
	private static Logger log = Logger.getLogger(LugarWebService.class
			.getName());

	public LugarWebService() throws DBException {
		lugarController = new LugarController();
	}

	@GET
	@Produces({ MediaType.APPLICATION_JSON })
	@Path("/getAll")
	public List<Lugar> getAllLugares() {
		try {
			return lugarController.getAll();
		} catch (DBException e) {
			// TODO: handle exception
			log.log(Level.SEVERE,
					null, e);
			throw new WSErrorException(e.getMessage());
		}
	}

	@POST
	@Produces({ MediaType.APPLICATION_JSON })
	@Consumes({ MediaType.APPLICATION_JSON })
	@Path("/getByFilters")
	/*
	 * public List<Lugar> getLugarByFilters(JSONObject requestLugarJSON,
	 * JSONObject requestSugeridoJSON, JSONObject requestRankingJSON, JSONObject
	 * requestLatitudJSON, JSONObject requestLongitudJSON) {
	 */
	public List<Lugar> getLugarByFilters(JSONObject requestLugarJSON) {
		List<Lugar> res = new LinkedList<Lugar>();
		List<Lugar> aux = new LinkedList<Lugar>();
		// Los parametros se obtienen como un objeto JSON y es necesario
		// realizar el
		// parseo

		// Objeto que almacenara la persona en formato JSON
		JSONObject lugarJSON;
		JSONObject tipoJSON;
		Boolean sugerido = Boolean.FALSE;
		Integer ranking = 0;
		Double latitud = 0.0;
		Double longitud = 0.0;
		Double distancia = 0.0;

		try {
			log.log(Level.INFO, "requestLugar = " + requestLugarJSON);

			/*
			 * Mapa para los filtros seleccionados Key = Filtro a utilizar,
			 * definido en la clase "DataBaseDictionary" Value = Sera un arreglo
			 * de dos posiciones donde: [0] = Valor a buscar en la base (Valor
			 * del campo) [1] = Tipo de Dato, deacuerdo a este se realizara el
			 * set en el PreparedStatement, Los tipos validos estan definidos en
			 * "DataBaseDictionary"
			 */
			Map<String, String[]> mapaConsulta = new HashMap<String, String[]>();
			String[] valor = new String[3];

			// Se obtiene el objeto JSON del REQUEST, mediante el nombre
			// de parametro ingresado
			if (requestLugarJSON.has(QueryParamDictionary.LUGAR_PARAM)) {
				lugarJSON = new JSONObject(
						requestLugarJSON
								.getString(QueryParamDictionary.LUGAR_PARAM));

				// Si tiene el nombre en los parametros
				if (lugarJSON.has("nombre")) {
					// Se agrega el nombre del lugar al mapa
					valor = new String[3];
					valor[0] = lugarJSON.getString("nombre");
					valor[1] = DataBaseDictionary.COLUMN_TYPE_LUGAR_NOMBRE;
					valor[2] = DataBaseDictionary.ALIAS_LUGAR;
					mapaConsulta.put(
							DataBaseDictionary.COLUMN_FILTER_NAME_LUGAR_NOMBRE,
							valor);

				}
				
				if(lugarJSON.has("id") && lugarJSON.getInt("id")!=0 ){
					// Se agrega el nombre del lugar al mapa
					valor = new String[3];
					valor[0] = lugarJSON.getInt("id")+"";
					valor[1] = DataBaseDictionary.COLUMN_TYPE_LUGAR_ID;
					valor[2] = DataBaseDictionary.ALIAS_LUGAR;
					mapaConsulta.put(
							DataBaseDictionary.COLUMN_FILTER_NAME_LUGAR_ID,
							valor);
				}
				// Si el objeto de lugar tiene un tipo agregado
				if (lugarJSON.has("tipo")) {
					tipoJSON = new JSONObject(lugarJSON.getString("tipo"));
					// Se verifica que cuente con el ID
					if (tipoJSON.has("id")) {
						int id = tipoJSON.getInt("id");
						if (id != 0) {
							// Se agrega el id del tipo del lugar al mapa
							valor = new String[3];
							valor[0] = id + "";
							valor[1] = DataBaseDictionary.COLUMN_TYPE_TIPO_LUGAR_ID;
							valor[2] = DataBaseDictionary.ALIAS_LUGAR;
							mapaConsulta
									.put(DataBaseDictionary.COLUMN_FILTER_NAME_TIPO_LUGAR_ID,
											valor);
						}
					}
				}
				log.log(Level.INFO, "LUGAR = " + lugarJSON);
			}

			// Si tiene el parametro de ranking
			if (requestLugarJSON.has(QueryParamDictionary.LUGAR_RANKING)) {
				ranking = Integer.parseInt(requestLugarJSON
						.getString(QueryParamDictionary.LUGAR_RANKING));
				// Se agrega el nombre del lugar al mapa
				valor = new String[3];
				valor[0] = ranking + "";
				valor[1] = DataBaseDictionary.COLUMN_TYPE_LUGAR_RANKING;
				valor[2] = DataBaseDictionary.ALIAS_LUGAR;
				mapaConsulta.put(
						DataBaseDictionary.COLUMN_FILTER_NAME_LUGAR_RANKING,
						valor);

			}
			log.log(Level.INFO, "Ranking = " + ranking);

			// Verifica si se pide que se retornen los lugares sugeridos por
			// amigos, lo cual implica buscar le ID de la persona
			if (requestLugarJSON.has(QueryParamDictionary.LUGAR_SUGERIDO)) {
				sugerido = Boolean.parseBoolean(requestLugarJSON
						.getString(QueryParamDictionary.LUGAR_SUGERIDO));

				// Si se desean los sugeridos
				if (sugerido) {
					// Se busca el ID de la persona como parametro
					if (requestLugarJSON
							.has(QueryParamDictionary.PERSONA_ID_PARAM)) {
						// Se agrega el nombre del lugar al mapa
						valor = new String[3];
						valor[0] = requestLugarJSON
								.getInt(QueryParamDictionary.PERSONA_ID_PARAM)
								+ "";
						valor[1] = DataBaseDictionary.COLUMN_TYPE_PERSONA_ID;
						valor[2] = DataBaseDictionary.ALIAS_LUGAR;						
						mapaConsulta
								.put(DataBaseDictionary.COLUMN_FILTER_NAME_PERSONA_ID,
										valor);
					} else {
						throw new WSErrorException("FALTA ID DE LA PERSONA");
					}
				}
 
			}
			log.log(Level.INFO, "Sugerido = " + sugerido);

			// Si tiene latitud y longitud se obtienen sus respectivos valores
			if (requestLugarJSON
					.has(QueryParamDictionary.LUGAR_POSICION_LATITUD)) {
				latitud = Double
						.parseDouble(requestLugarJSON
								.getString(QueryParamDictionary.LUGAR_POSICION_LATITUD));

			}
			log.log(Level.INFO, "Latitud = " + latitud);
			if (requestLugarJSON
					.has(QueryParamDictionary.LUGAR_POSICION_LONGITUD)) {
				longitud = Double
						.parseDouble(requestLugarJSON
								.getString(QueryParamDictionary.LUGAR_POSICION_LONGITUD));
			}
			log.log(Level.INFO, "Longitud = " + longitud);
			try {
				// Se realiza la consulta
				aux = lugarController.getByFilters(mapaConsulta);
				if (latitud != 0 & longitud != 0) {
					for (Lugar l : aux) {
						distancia = harvesine(l.getLatitud(), l.getLongitud(),
								latitud, longitud);
						log.log(Level.INFO, l.getNombre() + "=> D = "
								+ distancia);
						if (distancia <= 5) {
							res.add(l);
						}

					}
				}else{
					res.addAll(aux);
				}
				if (res.isEmpty()) {
					res.add(new Lugar());
				}
				return res;
			} catch (DBException e) {
				throw new WSErrorException(e.getMessage());
			}

		} catch (JSONException e) {
			// Si ocurre un error al parsear los parametros se retorna este
			// error al cliente
			log.log(
					Level.SEVERE, null, e);
			throw new WSErrorException("PARAMETROS INCORRECTOS");
		}

	}

	private double harvesine(Double latA, Double longA, Double latB,
			Double longB) {
		Double dLat;
		Double dLong;
		Double a;
		Double b;
		Double c;

		// Diferencia de longitudes
		dLat = latA - latB;
		dLong = longA - longB;

		// sin²(latA-latB)
		a = Math.pow(Math.sin(Math.toRadians(dLat) / 2), 2);
		// sin²(longA-longB)
		b = Math.pow(Math.sin(Math.toRadians(dLong) / 2), 2);

		// cos (latA) * cos(latB) * B
		c = Math.cos(Math.toRadians(latA)) * Math.cos(Math.toRadians(latB)) * b;

		// haversin(d/R)
		b = c + a;

		// d = 2 * R(Radio de la Tierra) * asin(sqrt(b))
		a = 2 * 6367.45 * Math.asin(Math.sqrt(b));
		return a;
	}
}
